﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace XMLParser
{
    class Attribute
    {
        public Attribute( XmlElement element )
        {
            _Tag = element.Name;
            _Name = ClassObject.InitCap( _Tag ).Replace( ":", "" );
            _Value = null;
            _NodeType = "element";
        }

        public Attribute( String tag, String type, String value, String listType )
        {
            _Tag = tag;
            _Name = ClassObject.InitCap( tag );
            _Type = type;
            _Value = value;
            _NodeType = listType;
            _ListType = listType;
            _NodeTag = tag;
        }

        public override int GetHashCode()
        {
            return _Tag.GetHashCode();
        }

        public override bool Equals( Object other )
        {
            return _Tag == ((Attribute)other)._Tag ? true : false;
        }

        public String _Tag;
        public String _Name;
        public String _Type;
        public String _Value;
        public String _ListType;
        public String _NodeType;
        public String _NodeTag;

        public String GetDecl( out String variableName )
        {
            String strType = _Type; 
            if( _ListType == "attr" || _ListType == "text" )
            {
                variableName = "str" + _Name ;
            }
            else if( _ListType == "list" )
            {
                variableName = "list" + _Name;
                strType = "${baseName}" + _Type + "List";
            }
            else
            {
                variableName =  _Name.ToLower() ;
                strType = "${baseName}" + _Type;
            }

            String strCode = "\n" + strType + "  " + variableName + " ; ";
            return strCode ;
        }

    }

    class ClassObject : Attribute
    {
        public ClassObject( String tag, String value, String listType ) :
            base( tag, ClassObject.InitCap( tag ), value, listType )
        {
            _listChildren = new List<ClassObject>();
            _listAttr = new List<Attribute>();

        }

        public override int GetHashCode()
        {
            return _Tag.GetHashCode();
        }

        public override bool Equals(Object other)
        {
            return _Tag == ((ClassObject) other)._Tag ? true : false;
        }

        private String _strCode;
        private String _strFreeMarkerAttributes;
        public List<Attribute> _listAttr;
        public List<ClassObject> _listChildren;

        public int AddChild(ClassObject child)
        {
            if( null == child )
            {
                return 0;
            }

            ClassObject attrCopy = _listChildren.Find( delegate( ClassObject attrib )
            {
                return attrib._Tag == child._Tag;
            } );

            if( null == attrCopy )
            {
                _listChildren.Add( child );
            }
            else
            {
                attrCopy._ListType = "list";
            }

            return 1;
        }

        public int Merge( ClassObject child )
        {
            if( child._listAttr.Count > 0 )
            {
                Attribute attr = child._listAttr [0];

                child._Type = attr._Type;
                if( attr._ListType == "list" )
                {
                    child._NodeType = "listElement";
                    attr._NodeTag = child._Tag;
                    AddAttribute( child );
                }
                else
                {
                    child._NodeTag = attr._Tag;
                    child._Type = attr._Type;
                    child._NodeType = "attrText";
                    AddAttribute( child );
                }
            }

            if( child._listChildren.Count > 0 )
            {
                ClassObject grandChild  = child._listChildren [0];
                grandChild._NodeType = "listElement";
                grandChild._NodeTag = child._Tag;
                AddChild( grandChild );
                child._listChildren.RemoveRange( 0, child._listChildren.Count );
            }

            return 0;
        }


        public int GetCount<T>( T child, List<T> collection)
        {
            int count = 0;
            if( null == child )
            {
                return count;
            }

            foreach( T item in collection )
            {
                if( child.Equals(item) )
                {
                    count++;
                }
            }

            return count;
        }

        public static String InitCap(String str)
        {
            String strCopy = str.Remove(0, 1);

            char firstLetter = Char.ToUpper(str[0]);
            String strfirstLetter = new String(firstLetter, 1);
            strCopy = strfirstLetter + strCopy;

            return strCopy;
        }

        public int AddAttribute( Attribute attr )
        {
            Attribute attrCopy = _listAttr.Find( delegate( Attribute attrib )
            {
                return attrib._Tag == attr._Tag;
            } );

            if( null == attrCopy )
            {
                _listAttr.Add( attr );
            }
            else
            {
                attrCopy._ListType = "list";
            }

            return 0;
        }

        public int AddAttribute( String tag, String type, String value, String listType )
        {
            Attribute attr = new Attribute( tag, type, value,listType );

            return AddAttribute( attr );
        }

        /// <summary>
        /// Check if Element has only one attr OR only one child Element.
        /// </summary>
        /// <returns></returns>
        public bool IsEmpty( )
        {
            int  count = _listAttr.Count + _listChildren.Count;
            if( _listAttr.Count == 1 && 1 == _listChildren.Count )
            {
                if( _listAttr[0]._Tag == _listChildren[0]._Tag )
                {
                    return true;
                }
            }

            return ( count > 1 ) ? false : true ;
        }

        public String Box(String key, String val)
        {
            String strCopy = Box(key) + ":" + Box(val);

            return strCopy;
        }

        public static String Box(String str)
        {
            String strCopy = "\"" + str + "\"" ;

            return strCopy;
        }

        public String GenerateParserCode(String functionTemplateFile)
        {
            // create reader & open file
            TextReader tr = new StreamReader( @"C:\Raman\code\CodeGen\CodePatterns\tdd\xml\function.cpp" );

            _strCode = _strFreeMarkerAttributes;
            _strCode += tr.ReadToEnd();
            tr.Close();

            //System.Console.WriteLine( _strCode );
            foreach( var item in _listChildren )
            {
                _strCode += item.GenerateParserCode( functionTemplateFile );
            }

            return _strCode;
        }

        public void Stream(String parentDir, String buffer)
        {
            if( IsEmpty() )
            {
                return;
            }
            // create a writer and open the file
            String strFilePath = parentDir + "\\" + _Name + ".hxx";

            TextWriter tw = new StreamWriter(strFilePath);

            //<#assign xml = "Channel">
            //<#assign Attributes = [ {"name":"Point", "type":"int", "facet":"sp_IN"}
            // {"name":"Point", "type":"double", "facet":"sp_IN"} 
            //] >

            _strFreeMarkerAttributes = "<#assign xml = " + Box(_Name) + "?cap_first >";
            _strFreeMarkerAttributes += "<#assign name = " + Box( _Name ) + "?cap_first >";
            _strFreeMarkerAttributes += "<#assign tag = " + Box( _Tag ) + " >";

            _strFreeMarkerAttributes += "\n<#assign Attributes = [\n";

            //RemoveDuplicateAttributes();

            for( int i = 0 ; i < _listAttr.Count ; i++ )
            {
                Attribute attr = _listAttr [i];

                String prefix = "", list = "n";
                if( attr._ListType == "list" )
                {
                    prefix = "List";
                    list = "y";
                }
                else if( attr._Type == "CATUnicodeString" )
                {
                    prefix = "str";
                }
                else if( attr._Type == "int" )
                {
                    prefix = "n";
                }
                else if( attr._Type == "double" )
                {
                    prefix = "d";
                }

                // {"name":"Point", "type":"double", "facet":"sp_IN"} 
                _strFreeMarkerAttributes += "\t { " + Box( "name", attr._Name ) + "," + Box( "type", attr._Type ) + "," + Box( "tag", attr._Tag) + ",";
                _strFreeMarkerAttributes += Box( "prefix", prefix ) + "," + Box( "list", list ) + "," ;
                _strFreeMarkerAttributes += Box( "nodeType", attr._NodeType ) + "," + Box( "nodeTag", attr._NodeTag );
                _strFreeMarkerAttributes += "}";

                _strFreeMarkerAttributes += "\n";
            }

            _strFreeMarkerAttributes += "] >" ;

            tw.WriteLine( _strFreeMarkerAttributes );
            tw.WriteLine(buffer);
            
            // close the stream
            tw.Close();

            foreach (var item in _listChildren)
            {
                item.Stream(parentDir, buffer);
            }
        }

        /// <summary>
        /// Add Child as attribute
        /// If a child is present more than once remove the extra instances
        /// </summary>

        public void RemoveDuplicateChildren()
        {
            int nChildrent = _listChildren.Count ;
            for( int i = nChildrent - 1 ; i >= 0 ; i-- )
            {
                ClassObject item = _listChildren[i] ;
                int count = GetCount<ClassObject>( item, _listChildren );

                AddAttribute( item );
                if( 1 < count )
                {
                    _listChildren.Remove( item );
                }
            }
        }

        public void Debug(String tab)
        {
            if( IsEmpty() )
            {
                return;
            }

            System.Console.WriteLine( tab + "Class " + _Name );

            RemoveDuplicateChildren();

            tab += "\t";
            for (int i = 0; i < _listAttr.Count; i++)
            {
                Attribute attr = _listAttr[i] ;

                if( attr._NodeType == "listElement" )
                {
                    ClassObject obj = (ClassObject) attr;
                    if( obj._listAttr.Count == 1 )
                    {
                        attr = obj._listAttr [0];
                    }
                }

                String prefix = "_";
                if( attr._ListType == "list" )
                {
                    prefix = " * _pList";
                }
                else if( attr._Type == "CATUnicodeString" )
                {
                    prefix = "_str";                    
                }
                else if( attr._Type == "int" )
                {
                    prefix = "_n";
                }
                else if( attr._Type == "double" )
                {
                    prefix = "_d";
                }

                System.Console.WriteLine( tab + attr._Type + "  " + prefix + attr._Name + " ; " );
            }

            foreach( var item in _listChildren )
            {
                item.Debug(tab);
            }
        }

        public String GenerateWriterCode( String functionTemplateFile )
        {
            // create reader & open file
            TextReader tr = new StreamReader( @"C:\Raman\code\CodeGen\CodePatterns\tdd\xml\WriterFunction.cpp" );

            _strCode = _strFreeMarkerAttributes;
            _strCode += tr.ReadToEnd();
            tr.Close();

            //System.Console.WriteLine( _strCode );
            foreach( var item in _listChildren )
            {
                _strCode += item.GenerateWriterCode( functionTemplateFile );
            }

            return _strCode;
        }

        public void WriteXML(  XmlDocument xmlDoc, XmlElement elem )
        {
            if( this._Value != null ) //  || item._NodeType == "element"
            {
                XmlText xmlTextNode = xmlDoc.CreateTextNode(this._Value) ;
                elem.AppendChild( xmlTextNode );
            }

            foreach( var item in _listAttr  )
            {
                Type typ = item.GetType();

                if( item.GetType().Name == "ClassObject" )
                {
                    ClassObject obj = (ClassObject) item;
                    if( obj.IsEmpty() )
                    {
                        XmlElement elemChild = xmlDoc.CreateElement( item._Tag );
                        elem.AppendChild( elemChild );

                        obj.WriteXML( xmlDoc , elemChild);
                        continue;
                    }
                }

                if( item._NodeType == "attr" )
                {
                    elem.SetAttribute( item._Tag, item._Value );
                }
                else if( item._Value != null ) //  || item._NodeType == "element"
                {
                    XmlText xmlTextNode = xmlDoc.CreateTextNode(item._Value) ;

                    if( item._Tag == this._Tag )
                    {
                        elem.AppendChild( xmlTextNode );
                    }
                    else
                    {
                        XmlElement elemChild = xmlDoc.CreateElement( item._Tag );
                        elemChild.AppendChild( xmlTextNode );

                        elem.AppendChild( elemChild );
                    }
                }
            }

            foreach( var child in _listChildren )
            {
                XmlElement elemChild = xmlDoc.CreateElement( child._Tag );

                elem.AppendChild( elemChild );

                child.WriteXML( xmlDoc, elemChild );
            }
        }
    
    }
}
